// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jaeger

import (
	"encoding/base64"
	"fmt"
	"gitee.com/go-wares/log/base"
	"gitee.com/go-wares/log/conf"
	"github.com/valyala/fasthttp"
	"net/http"
	"time"
)

// Reporter
// is a component that publish span to jaeger.
type Reporter struct {
	bucket    *base.Bucket[base.Span]
	formatter base.ReporterFormatter
}

// NewReporter
// creates a new reporter for jaeger.
func NewReporter() *Reporter {
	return (&Reporter{
		formatter: &Formatter{},
	}).init()
}

// GetBucket
// return tracer reporter bucket.
func (o *Reporter) GetBucket() *base.Bucket[base.Span] {
	return o.bucket
}

// SetFormatter
// set trace content formatter.
func (o *Reporter) SetFormatter(formatter base.ReporterFormatter) {
	o.formatter = formatter
}

// Publish
// trace span to jaeger.
func (o *Reporter) Publish(spans ...base.Span) {
	o.bucket.Add(spans...)
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *Reporter) init() *Reporter {
	o.bucket = base.NewBucket[base.Span]("jaeger-reporter", o.run)
	o.bucket.Start()
	return o
}

func (o *Reporter) run(spans []base.Span) {
	var (
		body     []byte
		c        = conf.Get()
		err      error
		request  = fasthttp.AcquireRequest()
		response = fasthttp.AcquireResponse()
		timeout  = time.Duration(c.JaegerTracer.Timeout) * time.Second
	)

	// Release
	// log items when write completed.
	defer func() {
		// Catch
		// runtime panic.
		if r := recover(); r != nil {
		}

		// Release items.
		for _, span := range spans {
			span.Release()
		}

		// Print error message.
		if err != nil {
			println("span report error:", err.Error())
		}

		// Release
		// request and response.
		fasthttp.ReleaseRequest(request)
		fasthttp.ReleaseResponse(response)
	}()

	// Format spans of the trace as bytes.
	if body = o.formatter.Format(spans); body == nil {
		return
	}

	// Prepare request.
	request.SetRequestURI(c.JaegerTracer.Endpoint)
	request.SetBody(body)
	request.Header.SetMethod(http.MethodPost)
	request.Header.SetContentType("application/x-thrift")

	// BasicAuth
	if usr := c.JaegerTracer.Username; usr != "" {
		pwd := c.JaegerTracer.Password
		request.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(usr+":"+pwd))))
	}

	// Send http request.
	if err = fasthttp.DoTimeout(request, response, timeout); err == nil {
		if code := response.StatusCode(); code != http.StatusOK && code != http.StatusAccepted {
			err = fmt.Errorf(`HTTP %d %s`, code, http.StatusText(code))
			return
		}
	}
}
